/**
 * 组件记忆化系统
 *
 * 实现类似React.memo的组件优化功能，避免不必要的重新渲染
 * 通过缓存上一次渲染结果，在props未变化时直接返回缓存结果
 */

import { ReactElement } from "../createElement";
import { componentHooks, setCurrentComponent } from "../hooks";

/**
 * 默认属性比较函数
 *
 * 执行属性的浅比较，用于判断组件是否需要重新渲染
 *
 * @param prevProps 前一次渲染的属性
 * @param nextProps 当前渲染的属性
 * @returns 如果属性相等返回true，否则返回false
 */
function defaultPropsAreEqual(prevProps: any, nextProps: any): boolean {
  // 引用相同时快速返回
  if (prevProps === nextProps) return true;

  // 比较属性键数量
  const prevKeys = Object.keys(prevProps || {});
  const nextKeys = Object.keys(nextProps || {});

  if (prevKeys.length !== nextKeys.length) return false;

  // 逐一比较所有属性值
  for (const key of prevKeys) {
    // 子元素特殊处理
    if (key === "children") {
      const prevChildren = prevProps.children;
      const nextChildren = nextProps.children;

      // 数组类型子元素比较
      if (Array.isArray(prevChildren) && Array.isArray(nextChildren)) {
        if (prevChildren.length !== nextChildren.length) return false;

        // 浅比较子元素引用
        for (let i = 0; i < prevChildren.length; i++) {
          if (prevChildren[i] !== nextChildren[i]) return false;
        }
      }
      // 单个子元素比较
      else if (prevChildren !== nextChildren) {
        return false;
      }
    }
    // 普通属性比较
    else if (!Object.is(prevProps[key], nextProps[key])) {
      return false;
    }
  }

  return true;
}

/**
 * 记忆化组件高阶函数
 *
 * 缓存组件渲染结果并在属性未变化时跳过重新渲染，提高性能
 *
 * @template P 组件属性类型
 * @param Component 要记忆化的函数组件
 * @param areEqual 可选的自定义属性比较函数
 * @returns 记忆化后的组件函数
 */
export function memo<P extends object>(
  Component: (props: P) => ReactElement,
  areEqual: (prevProps: P, nextProps: P) => boolean = defaultPropsAreEqual
): (props: P) => ReactElement {
  // 记忆化状态
  let prevProps: P | null = null;
  let prevResult: ReactElement | null = null;
  let memoizedComponent: any = null;

  // 记忆化组件实现
  const MemoComponent = (props: P): ReactElement => {
    // 判断是否需要重新渲染:
    // 1. 首次渲染
    // 2. 无缓存结果
    // 3. 属性发生变化
    if (!prevProps || !prevResult || !areEqual(prevProps, props)) {
      // 创建新的组件实例
      memoizedComponent = {};

      // 设置当前组件上下文
      setCurrentComponent(memoizedComponent);

      try {
        // 执行原始组件函数
        const result = Component(props);

        // 缓存结果和属性
        prevProps = { ...props };
        prevResult = result;

        return result;
      } finally {
        // 清理组件上下文
        setCurrentComponent(null);
      }
    }

    // 属性未变化，直接返回缓存结果
    return prevResult;
  };

  // 设置组件显示名称，便于调试
  MemoComponent.displayName = `Memo(${Component.name || "Component"})`;

  // 添加手动更新方法
  MemoComponent.update = () => {
    if (memoizedComponent && componentHooks.has(memoizedComponent)) {
      prevProps = null; // 清除缓存，强制重新渲染
      return MemoComponent(prevProps || ({} as P));
    }
  };

  return MemoComponent;
}
